#ifndef __BOUNDING_VOLUME_H__
#define __BOUNDING_VOLUME_H__

#pragma once

#include "Matrix4x4.h"
#include "Quaternion.h"

namespace Nezha
{
	/** aligned axis bounding box. */
	class NEZHA_ENGINE_ENTRY AABB
	{
	public:
		AABB()
		{
			reset();
		}

		AABB(const Vector3& ext)
		{
			mMin = -ext;
			mMax = ext;
		}

		AABB(const Vector3& _min, const Vector3& _max)
		{
			mMin = _min;
			mMax = _max;
		}

		AABB(const AABB& aabb)
		{
			mMin = aabb.mMin;
			mMax = aabb.mMax;
		}

		bool _notValid() const
		{
			return mMin.x > mMax.x;
		}

		bool notValid() const
		{
			return mMin.x > mMax.x || mMin.y > mMax.y || mMin.z > mMax.z;
		}

		bool empty() const
		{
			return mMax == mMin;
		}

		Vector3 getCenter() const
		{
			return (mMin + mMax) * 0.5f;
		}

		Vector3 getSize() const
		{
			return (mMax - mMin) * _guaranteeValid(1.0f, 0.0f);
		}

		float getRadius() const
		{
			return _guaranteeValid((mMax - mMin).length() * 0.5f, 0);
		}

		float getSqrRadius() const
		{
			return _guaranteeValid(((mMax - mMin) * 0.5f).sqrLength(), 0);
		}

		float getVolume() const
		{
			float fvolume = (mMax.x - mMin.x) * (mMax.y - mMin.y) * (mMax.z - mMin.z);
			return _guaranteeValid(fvolume, 0);
		}

		void merge(const Vector3& v);
		void mergeSphere(const Vector3& c, float r);
		void mergeAABB(const AABB& aabb);

		void fit2(const AABB& aabb);

		// make AABB invalid
		void reset()
		{
			mMin.set(NZ_INFINITY, NZ_INFINITY, NZ_INFINITY);
			mMax.set(-NZ_INFINITY, -NZ_INFINITY, -NZ_INFINITY);
		}

		void translate(const Vector3& v);

		void expand(const Vector3& v)
		{
			if(!_notValid())
			{
				mMin -= v;
				mMax += v;
			}
		}

		bool intersect(const Vector3& p) const
		{
			if(empty() || _notValid())
				return false;

			if (p.x < mMin.x) return false;
			if (p.y < mMin.y) return false;
			if (p.z < mMin.z) return false;
			if (p.x > mMax.x) return false;
			if (p.y > mMax.y) return false;
			if (p.z > mMax.z) return false;

			return true;
		}

		// sphere
		bool intersectSphere(const Vector3& c, float r) const;

		bool intersectPlane(const Vector3& normal, float d) const;

		bool intersectAABB(const AABB& aabb) const;

		// helper
		float _guaranteeValid(float ifValid, float ifNotValid) const
		{
			float t = mMax.x - mMin.x;
			return t < 0.0f ? ifNotValid : ifValid;
		}

		void rebuildAffine(const Matrix4x4& affineM);

		void rebuild(const Matrix4x4& mat);

		/** just only transform with rotation and translation */
		AABB transform(const Matrix3x3& rot, const Vector3& trans) const;

		/** just only transform with rotation and translation */
		AABB transform(const Matrix4x4& affineM) const;

		/** just only transform with rotation. */
		AABB transform(const Quaternion& quat) const;

		// TODO from OBB

		void fromVertexsData(u32 numVerts, u32 offsetInVertex, u32 stride, u8* data);

NEZHA_INTERNAL:
		Vector3 mMin;
		Vector3 mMax;
	};


	class NEZHA_ENGINE_ENTRY OBB
	{
	public:
		inline OBB(){}

		OBB(const Matrix3x3& rot, const Vector3& h, const Vector3& c)
			:mRot(rot)
			,mHalfLen(h)
			,mCenter(c)
		{
		}

		void fromAABB(const Matrix3x3& rot, const AABB& aabb)
		{
			mRot = rot;
			mHalfLen = (aabb.mMax - aabb.mMin) * 0.5f;
			mCenter = (aabb.mMax + aabb.mMin) * 0.5f;
		}

		void fromAABB(const Quaternion& quat, const AABB& aabb)
		{
			quat.toRotation(mRot);
			mHalfLen = (aabb.mMax - aabb.mMin) * 0.5f;
			mCenter = (aabb.mMax + aabb.mMin) * 0.5f;
		}

		Matrix3x3 mRot;
		Vector3 mHalfLen;
		Vector3 mCenter;
	};

}//end namespace Nezha

#endif //end __BOUNDING_VOLUME_H__